﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OAuth2.Framework;
using System.Collections.Specialized;
using OAuth2.Framework.Utility;
using System.Net;
using System.IO;
using Newtonsoft.Json;

namespace OAuth2.Client
{
    public class TokenRequest
    {
        public Uri TokenEndpoint { get; private set; }

        public TokenRequest(Uri tokenEndpoint) {
            TokenEndpoint = tokenEndpoint;
        }

        /// <summary>
        /// 更新Token
        /// </summary>
        /// <param name="client"></param>
        /// <param name="refreshToken"></param>
        /// <returns></returns>
        public IToken RefreshAccessToken(IClient client, string refreshToken) {
            AssertClientPresent(client);
            if (string.IsNullOrWhiteSpace(refreshToken))
            {
                throw new ArgumentNullException("refreshToken");
            }

            NameValueCollection components = new NameValueCollection() { 
                {Parameters.ClientId,client.ClientID},
                {Parameters.ClientSecret,client.ClientSecret},
                {Parameters.RefreshToken,refreshToken},
                {Parameters.GrantType,Parameters.GrantTypeValues.ResfreshToken}
            };

            return FetchToken(components);
        }

        public IToken ExchangeClientCredentials(IClient client) {
            AssertClientPresent(client);

            NameValueCollection components = new NameValueCollection() { 
                {Parameters.ClientId,client.ClientID},
                {Parameters.ClientSecret,client.ClientSecret},
                {Parameters.GrantType,Parameters.GrantTypeValues.ClientCredentials}
            };

            return FetchToken(components);
        }

        public IToken ExchangeResourceOwnerCredentials(IClient client,string ResourceOwnerUsername,string ResourceOwnerPassword) {
            AssertClientPresent(client);
            if (string.IsNullOrWhiteSpace(ResourceOwnerUsername))
            {
                throw new ArgumentNullException("resourceOwnerUsername");
            }
            if (string.IsNullOrWhiteSpace(ResourceOwnerPassword))
            {
                throw new ArgumentNullException("resourceOwnerPassword");
            }

            NameValueCollection components = new NameValueCollection() { 
                {Parameters.ClientId,client.ClientID},
                {Parameters.ClientSecret,client.ClientSecret},
                {Parameters.GrantType,Parameters.GrantTypeValues.Password},
                {Parameters.ResourceOwnerUsername,ResourceOwnerUsername},
                {Parameters.ResourceOwnerPassword,ResourceOwnerPassword}
            };
            return FetchToken(components);

        }

        public IToken ExchangeAuthorizationGrant(IClient client, string code, Uri RedirectUri)
        {
            AssertClientPresent(client);

            if (string.IsNullOrWhiteSpace(code)) {
                throw new ArgumentNullException("code");
            }
            if (RedirectUri == null) {
                throw new ArgumentNullException("redirectUri");
            }
            NameValueCollection components = new NameValueCollection() { 
                {Parameters.ClientId,client.ClientID},
                {Parameters.ClientSecret,client.ClientSecret},
                {Parameters.GrantType,Parameters.GrantTypeValues.AuthorizationCode},
                {Parameters.AuthroizationCode,code},
                {Parameters.RedirectUri,RedirectUri.AbsolutePath}
            };

            return FetchToken(components);
        }

        #region Private Method

        private static void AssertClientPresent(IClient client) {
            if (string.IsNullOrWhiteSpace(client.ClientID))
            {
                throw new ArgumentNullException("ClientID");
            }
            if (string.IsNullOrWhiteSpace(client.ClientSecret))
            {
                throw new ArgumentNullException("ClientSecret");
            }
        }

        private IToken FetchToken(NameValueCollection components)
        {
            string postData = UriHelper.ReconstructQueryString(components);
            byte[] data = System.Text.ASCIIEncoding.ASCII.GetBytes(postData);

            WebRequest request = WebRequest.Create(TokenEndpoint);

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;
            Stream reqSream = request.GetRequestStream();
            reqSream.Write(data, 0, data.Length);
            reqSream.Close();

            try
            {
                WebResponse response = request.GetResponse();

                string accessToken = string.Empty;

                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    accessToken = sr.ReadToEnd();
                }
                Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(accessToken);

                return new AccessToken(dictionary);
            }
            catch (WebException)
            {
                throw;
            }
        }
        #endregion 

    }
}
